こんにちは、柴田です。
Next.js 9.3からPreview Modeがサポートされました。
Preview Modeは完全にヘッドレスCMS向けに作られた機能です。
静的生成によるJamstack構成は高パフォーマンス、高セキュリティを実現しますが、プレビューの実現には工夫する必要がありました。
下書き状態の記事を確認する際に、その都度ビルドを走らせてページを生成するのは時間がかかりすぎてしまうためです。
そこで今回のNext.jsによるPreview Modeの登場です。
Preview Modeを利用すると静的生成ロジックをうまく利用してサーバーサイドレンダリング(SSR)を行い、プレビューを実現することができます。
API Routes
まず前提として、Next.jsにはサーバーレス関数を起動できるAPI Routes機能があります。
`/pages/api/`以下のファイルはサーバーレス関数として動作します。
Preview Modeではこの機能を利用します。
プレビュー用関数の作成
次のような関数(/pages/api/preview.js)を用意します。
import fetch from 'node-fetch';
export default async (req, res) => {
if (!req.query.slug) {
return res.status(404).end();
}
const content = await fetch(
`https://xxxxxx.microcms.io/api/v1/blog/${req.query.slug}?fields=id&draftKey=${req.query.draftKey}`,
{ headers: { 'X-MICROCMS-API-KEY': process.env.apiKey || '' } }
)
.then(res => res.json()).catch(error => null);
if (!content) {
return res.status(401).json({ message: 'Invalid slug' });
}
res.setPreviewData({
slug: content.id,
draftKey: req.query.draftKey,
});
res.writeHead(307, { Location: `/${content.id}` });
res.end('Preview mode enabled');
};
ブラウザから `/api/preview` にアクセスするとこの関数が起動します。
クエリとして渡ってくる slug は記事のID、draftKey は下書き用の draftKey に当たります。
まず、slugが存在しない場合は404を返します。
公式のチュートリアルでは、CMSとNext.js間で共通鍵による認証もしています。
よりセキュアにしたい場合はその処理を入れてください。
次に、slugが正しいものであるか実際にmicroCMSのAPIを呼び出してチェックします。
その後、`res.setPreviewData()` に渡した引数が `getStaticProps` の引数である `context` から受け取ることができます。
最後に、本来の記事のパスにリダイレクトさせます。
この際に、オープンリダイレクトの脆弱性回避のために、`req.query.slug` ではなく `content.id` を使うようにします。
リダイレクト後はプレビュー用に `__prerender_bypass` と `__next_preview_data` というcookieが付与されます。
ページ側での処理
リダイレクトして記事ページに辿り着いた後は、`getStaticProps` が動作します。
先ほどのcookie付きでこのページに辿り着いた場合、`getStaticProps` の引数である `context` に対し、
- `context.preview` はtrue
- `content.previewData` には `setPreviewData` に与えた引数
が格納されています。
これにより `slug` と `draftKey` を受け取ることができるので、microCMSのGET APIを叩く際にクエリとして指定し、下書き状態のコンテンツを取得することができます。
// pages/[slug].tsx
export const getStaticProps = async (context) => {
const slug = context.params?.slug;
const draftKey = context.previewData?.draftKey;
const content = await fetch(
`https://xxxxxx.microcms.io/api/v1/blog/${slug}${
draftKey !== undefined ? `?draftKey=${draftKey}` : ''
}`,
{ headers: { 'X-MICROCMS-API-KEY': process.env.apiKey || '' } }
)
.then((res) => res.json());
return {
props: {
content
}
};
};
また、`getStaticPaths` にも変更が必要です。
返り値のオブジェクトで `fallback: false` としてしまうと、公開済みのページは良いですが下書き状態のページは未生成のため、`/api/preview` からのリダイレクト時に404となってしまいます。
よって、ここでは `fallback: true` と指定しましょう。
この副作用として、コンテンツがない場合にもページのレンダリング処理が通ってしまうので、コンポーネント側でエラーハンドリングをしましょう。
const Page: NextPage<Props> = ({ content }) => {
if (!content) {
return <ErrorPage />;
}
// 略
};
ここまでの実際の動きは以下のようになります。
- `/api/preview?slug={slug}&draftKey={draftKey}` にアクセス
- プレビュー用のcookieが発行され、`/{slug}` にリダイレクト
- `getStaticProps` で `slug` と `draftKey` を受け取ることができるので、下書き情報を取得
- 画面に表示
また、プレビューかどうかのフラグがあるので、画面側で「現在プレビュー中です」などの表示も可能です。
Cookieを無効にするためにはCookieクリア用のAPI routeを別で用意し、画面から呼び出すのが良いでしょう。
export default (req, res) => {
res.clearPreviewData();
}
CMS側の設定
次にmicroCMS側からプレビュー画面へのつなぎ込み設定を行います。
API設定 > 画面プレビューから設定が可能です。
ここでは、コンテンツ編集画面の画面プレビューリンクをクリックした際の遷移先を指定できます。
先ほどのプレビュー機能を用いるために、以下のように入力します。
https://some-blog-domain.com/api/preview?slug={CONTENT_ID}&draftKey={DRAFT_KEY}
以上でつなぎ込みは完了です。
下書きの状態でコンテンツ編集画面の画面プレビューボタンをクリックすることでプレビュー画面を閲覧することができます。
デプロイ
API Routes機能はサーバーレス関数を起動するため、デプロイ先のサーバーはホスティングだけでなく、サーバーロジックの処理を行う必要があります。
Next.jsのPreview Modeでは内部の処理でcookieの付与等も行うため、この機能を完全に活かすためにはVercel (Now)にデプロイするのが最適でしょう。
Vercelとは
VercelはNext.jsを開発しているVercel社(旧ZEIT)のサービスで、Webサイトのホスティング機能やサーバーレス関数をデフォルトで備えている特徴があります。
SSR、SPA、SSGのすべてに対応している点が強いですね。
また、個人開発は基本的に全て無料で使うことができます。(GitHubのOrganization連携は無料プランではできませんでした)
方法
Gitの連携か、nowコマンドによるcliデプロイのどちらかになります。
Git連携はVercelのダッシュボードにしたがって進めればOKです。
コマンドでやる場合は以下の3コマンドでいけます。
$ npm i -g now
// メールアドレス入力、確認
$ now login
// カレントディレクトリをデプロイ
$ now
Vercel側の設定画面で、ビルドコマンド等を指定する箇所がありますが、ビルドコマンドを特に変更していない場合はそのままでOKです。
※ `npm run build && npm run export` と入力してしまうと、API Routesが動かなくなるのでお気をつけください。
おわりに
Next.jsのPreview Modeを使って、microCMSのプレビュー機能を実装してみました。
今まではプレビュー用にルーティングをし、クライアントサイドから下書き用のAPIを呼び出してプレビュー画面を用意していましたが、その必要がなくなりました。
プレビュー画面へのアクセスに対してはサーバーサイドで判定を行うことができるため、よりセキュアになります。
本番環境をそのまま下書きプレビュー環境にできるので素晴らしいですね!
ぜひ試してみてください。
-----
microCMSは日々改善を進めています。
ご意見・ご要望は管理画面右下のチャット、公式Twitter、メールからお気軽にご連絡ください!
引き続きmicroCMSをよろしくお願いいたします!